﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using XL_KVMC;
using XL_KVMC.Protocols;

namespace XL_KVMC
{
    public class MCKV : IDevices
    {
        private ICommunications _comm { get; }
        public MCKV(ICommunications comm) { _comm = comm; }

        public bool Open()
        {
            _comm.SetProtocol(new Protocols.KVMC_Protocol(McFrame.MC3E));
            return _comm.Open();
        }

        public void Close() => _comm.Close();

        public Task Read()
        {
            throw new NotImplementedException("未实现该方法");
        }

        public byte[] Read(int length)
        {
            throw new NotImplementedException("未实现该方法");
        }

        public TClass? ReadClass<TClass>(int address, TClass obj = null, int count = 3) where TClass : class
        {
            if (obj == null) { obj = Activator.CreateInstance<TClass>(); }
            int classSize = (int)StructToBytes.GetClassSize(obj);
            if (classSize > 1998)
            {
                throw new Exception("超过Fins最大字节数");
            }
            List<byte> command = new List<byte>()
            {
                0x01,
                0x04,
                0,
                0,
                (byte) address,
                (byte) (address >> 8),
                (byte) (address >> 16),
                (byte) 0xA8,
                (byte) classSize,
                (byte) (classSize >> 24),
            };
            var bytes = _comm.SendToRead(command.ToArray(), count);
            if (bytes == null) { return default; }
            int boolSize = 0;
            StructToBytes.FromBytes(obj, bytes, ref boolSize, 0);
            return obj;
        }

        public TValue ReadSingle<TValue>(int address, int length, int count = 3)
        {
            List<byte> command = new List<byte>()
            {
                0x01,
                0x04,
                0,
                0,
                (byte) address,
                (byte) (address >> 8),
                (byte) (address >> 16),
                (byte) 0xA8,
            };
            Type type = typeof(TValue);
            if (type.IsArray)
            {
                throw new Exception("请使用ReadClass<TClass>读取数组数据");
            }
            int count_length = 0;
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    count_length = 2;
                    command.Add(2);
                    command.Add(0);
                    break;
                case "Double":
                    count_length = 4;
                    command.Add(4);
                    command.Add(0);
                    break;
                case "Int16":
                case "UInt16":
                    count_length = 1;
                    command.Add(1);
                    command.Add(0);
                    break;
                case "Int64":
                case "UInt64":
                case "String":
                    count_length = length / 2;
                    command.Add((byte)count_length);
                    command.Add((byte)(count_length >> 8));
                    break;
                case "Byte":
                case "Boolean":
                    throw new Exception($"没有实现 {type.Name}");
            }
            byte[] bytes = _comm.SendToRead(command.ToArray(), 3);
            if (type.Name == "String")
            {
                byte b = bytes[2];
                for (int i = 3; i < bytes[0]; i = i + 2)
                {
                    byte t = bytes[i - 1];
                    bytes[i - 1] = bytes[i];
                    bytes[i] = t;
                }
                return (TValue)(object)Encoding.ASCII.GetString(bytes.Skip(2).ToArray());
            }
            return (TValue)StructToBytes.GetValue(type, bytes, 0);
        }

        public byte[] Write(byte[] buffer)
        {
            throw new NotImplementedException("未实现该方法");
        }

        public bool WriteClass<TClass>(TClass value, int address, int count = 3) where TClass : class
        {
            if (value == null) { throw new Exception("不要传递空对象"); }
            int classSize = (int)StructToBytes.GetClassSize(value);
            if (classSize > 1998)
            {
                throw new Exception("超过Fins最大字节数");
            }
            byte[] array = new byte[classSize];
            List<byte> command = new List<byte>()
            {
                0x01,
                0x14,
                0,
                0,
                (byte) address,
                (byte) (address >> 8),
                (byte) (address >> 16),
                (byte) 0xA8,
            };
            int boolSize = 0;
            StructToBytes.ToBytes(value, array, ref boolSize, 0);
            int length = classSize / 2;
            command.Add((byte)length);
            command.Add((byte)(length >> 8));
            command.AddRange(array);
            var bytes = _comm.SendToRead(command.ToArray(), 3);
            return bytes != null;
        }

        public bool WriteSingle(object value, int address, int offset, int length, int count = 3)
        {
            Type type = value.GetType();
            if (type.IsArray)
            {
                throw new Exception("请使用WriteClass<TClass>方法写入数组数据");
            }
            List<byte> command = new List<byte>()
            {
                0x01,
                0x14,
                0,
                0,
                (byte) address, 
                (byte) (address >> 8),
                (byte) (address >> 16),
                (byte) 0xA8,
            };
            byte[] bytes = null;
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    command.Add(2);
                    command.Add(0);
                    bytes = new byte[4];
                    break;
                case "Double":
                    command.Add(4);
                    command.Add(0);
                    bytes = new byte[8];
                    break;
                case "Int16":
                case "UInt16":
                    command.Add(1);
                    command.Add(0);
                    bytes = new byte[2];
                    break;
                case "Byte":
                case "Int64":
                case "UInt64":
                case "Boolean":
                    throw new Exception($"没有实现 {type.Name}");
                case "String":
                    byte[] code = Encoding.ASCII.GetBytes((string)value);
                    List<byte> list2 = new List<byte>();
                    list2.Add((byte)length);
                    list2.Add((byte)code.Length);
                    list2.AddRange(code);
                    if (list2.Count % 2 != 0)
                    {
                        list2.Add(0);
                    }
                    for (int i = 3; i < list2.Count; i = i + 2)
                    {
                        byte t = list2[i - 1];
                        list2[i - 1] = list2[i];
                        list2[i] = t;
                    }
                    bytes = list2.ToArray();
                    int l = bytes.Length;
                    command.Add((byte)(l / 2));
                    command.Add((byte)((l / 2) >> 8));
                    break;
            }
            if (type.Name != "String")
            {
                StructToBytes.GetBytes(value, bytes, 0);
            }
            command.AddRange(bytes);
            bytes = _comm.SendToRead(command.ToArray(), 3);
            return bytes != null;
        }
    }
}
